package com.tianhao.luo.atomic;

import sun.misc.Unsafe;

/**
 * 提供原子性boolean的保存和读取操作
 *
 * @author tianhao.luo@hand-china.com
 */
public class MyAtomicBoolean {
    // Java和C++语言的一个重要区别就是Java中我们无法直接操作一块内存区域，不能像C++中那样可以自己申请内存和释放内存。
    // Java中的Unsafe类为我们提供了类似C++手动管理内存的能力。

    // Unsafe支持的操作如下
    // 1 偏移量相关：获取对象属性和静态属性的偏移量；获取数组的第一个元素的偏移量地址（arrayBaseOffset），元数据的增量地址（arrayIndexScale）
    // 2 线程调度：挂起线程，唤醒线程，monitorEnter，monitorExit
    // 3 类加载：定义类，创建对象，定义匿名内部类，确保一个类被加载，判断是否加载一个类
    // 4 内存屏障：读屏障，写屏障，读写屏障
    // 5 普通读写：读取一个object对象的field，直接从内存中的一个地址读写
    // 6 volatile读写：volatile读写一个object的field，volatile可以保证可见性和有序性
    // 7 有序写：有序写一个Object的field，保证有序性不保证可见性
    // 8 内存直接操作：申请内存，重新申请内存，释放内存，内存复制
    // 9 cas相关：提供int，long和object的cas操作

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    /**
     * 内存偏移量
     */
    private static final long valueOffset;

    static {
        try {
            // 初始化value属性在内部的偏移量
            valueOffset = unsafe.objectFieldOffset(MyAtomicBoolean.class.getDeclaredField("value"));
        } catch (NoSuchFieldException e) {
            throw new Error(e);
        }
    }

    /**
     * 实际存储值
     */
    private volatile int value;

    public MyAtomicBoolean(boolean initValue) {
        // true代表1，false代表0
        this.value = initValue ? 1 : 0;
    }

    public MyAtomicBoolean() {
    }


    /**
     * 获取当前的值
     *
     * @return 当前value是否为true
     */
    public final boolean get() {
        return value == 1;
    }


    /**
     * 对value进行cas更新操作
     *
     * @param expect 预计值
     * @param update 符合预期就更新的值
     * @return 保存成功返回true
     */
    public final boolean compareAndSet(boolean expect, boolean update) {
        // 将boolean转化为int
        int e = expect ? 1 : 0;
        int u = update ? 1 : 0;

        // 对value进行更新采用cas操作
        // cas方法的参数含义：
        // 第一个：待更新的对象
        // 第二个：对象待更新属性在内存中的偏移量
        // 第三个：预期值
        // 第四个：符合预期值，就替换的值
        boolean b = unsafe.compareAndSwapInt(this, valueOffset, e, u);
        return b;
    }


    /**
     * 直接操作volatile修饰的对象，由于是内存可见的，可以认为这是原子性的操作
     *
     * @param newValue 待保存值
     */
    public final void set(boolean newValue) {
        value = newValue ? 1 : 0;
    }

    /**
     *
     * 返回当前的value值，并更新value值为新的
     *
     * @param newValue 将要更新的值
     * @return 更新前的value值
     */
    public final boolean getAndSet(boolean newValue) {
        boolean prev;
        do {
            // 获取当前的value值对应的布尔值
            prev=get();
        }
        // cas更新操作不成功就继续获取当前值进行cas更新操作
        while (!compareAndSet(prev,newValue));
        return prev;
    }

}
